class: center, middle, inverse, title-slide # Aplicaciones con geo-datos: Parte II ## Curso: Introducción al análisis espacial y web-mapping
con Google Earth Engine y R Shiny
### MSc. José A. Lastra
Matías Olea
### Laboratorio Geo-Información y Percepción Remota ### 11/07/2021 --- background-image: url(data:image/png;base64,#logo_labgrs_color.png) background-position: center background-size:40% --- Librerías utilizadas en esta sesión ```r library(shiny) library(leaflet) library(tidyverse) library(raster) library(plotly) library(shinythemes) library(ggfortify) ``` -- A continuación, dispone de todas las líneas de código para seguir el tutorial de manera complementaria. --- class: middle, center <iframe width="800" height="500" src="https://www.youtube.com/embed/T3V6URxERhI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> --- # Aplicación de geodatos con raster -- - Como ya vimos, shiny nos otorga flexibilidad para el trabajo con datos vectoriales y leaflet -- - Ahora utilizaremos información raster para usar las reacciones de mapa sobre la información. -- - En esta aplicación usaremos datos satelitales de temperatura superficial del mar (SST) mensual MODIS Aqua, 4 km. -- - Las imágenes las podrá encontrar en la carpeta *monthly_sst* y las tablas en la carpeta *tables* - La carpeta de imágenes tiene 204 archivos en formato *GeoTiff* desde enero de 2003 hasta diciembre de 2019 (medianas mensuales) - En la carpeta de tablas disponemos del archivo de fechas *allDates_sst.csv* y un archivo de nombre *pixels.RData* que contiene la información de todos los pixeles de SST en formato data frame. - Además de una serie de tiempo que emplearemos en el segundo tab de nuestra página. - Disponga de estos en una carpeta de nombre *data* en el directorio de su aplicación -- - *Recuerde*: revisar las librerías necesarias para la aplicación e instalarlas antes de comenzar. --- #Estructura de la aplicación -- - Esta vez veremos otro tipo de estrucura disponible que es el *navbarPage()* -- - Crearemos dos páginas una con un mapa full page empleando *tags* y la función *div()*. La otra tendrá un módulo de adicional para subir datos. -- - Configuraremos la interfaz inicial y leeremos los datos raster empleando una lista para optimizar el despliegue de la aplicación --- # UI ```r library(shiny) library(leaflet) library(tidyverse) library(raster) library(plotly) library(shinythemes) library(ggfortify) ##########UI#################### ui <- navbarPage(title = "SST data",theme = shinytheme('cerulean'), #primer panel tabPanel(title = "Mapa principal", div(class='outer', #configuración de mapa a pantalla completa tags$style(type = "text/css",".outer {position: fixed; top: 41px; left: 0; right: 0;bottom: 0;overflow: hidden; padding: 0}"), leafletOutput(outputId = "map", width = "100%", height = "100%"), # Main map ) ), #segundo panel tabPanel(title = "Tab secundario") ) ``` -- - La libreria [shinythemes](https://github.com/rstudio/shinythemes) extiende las posibilidades estéticas sin necesidad de agregar código por nuestra cuenta. -- - Más adelante veremos la librería [bslib](https://github.com/rstudio/bslib), que es una mejora de las últimas actualizaciones --- # Server ```r server <- function(input, output, session) { #fechas dates.table <- read_csv('data/tables/allDates_sst.csv')#leer archivo con fechas dates <- dates.table$x #columna de fechas a objeto individual #raster data sst.list <- list.files(path = 'data/monthly_sst/', pattern = glob2rx('*sst*.tif'),full.names = T) #lista de archivos TIF #load pixels data load('data/tables/pixels.RData') #carga de tabla con información de pixeles } #Compile shinyApp(ui, server) ``` -- .center[  ] --- # Agregando entradas y salidas -- - Primero agregaremos un panel absoluto luego de nuestro mapa de leaflet donde pondremos las opciones -- - Añadiremos la opción *uiOutput()* que permitirá renderizar una entrada reactiva a partir de información disponible en el servidor. -- ```r absolutePanel(id = "controls", class = "panel panel-default", fixed = F, draggable = F, top = 90, left = "auto", right = 20, bottom = "auto", width = 300, height ="auto", style="z-index:500;", uiOutput('fechasInput')) #salida reactiva con datos del server ``` -- - La salida, de nombre *fechasInput*, será renderizada a partir de nuestra tabla de fechas cargada en el server para limitar las opciones y no producir errores al no existir datos. Esto se puede dar si empleamos el widget *dateInput()* -- - Para esto agregaremos la función *renderUI({})* luego de la carga de nuestra información. --- #Render UI -- - Usaremos el objeto *dates* creado al inicio del server y usaremos ese listado para manipular nuestros datos -- - Ordenaremos las fechas de la más actual a la más antigua con la función *rev()* -- ```r #renderizar la ui output$fechasInput <- renderUI({ #render de fechas con objeto del server selectInput(inputId = 'fechas',label = 'Seleccione una fecha', choices = rev(dates),selected = tail(dates,1)) }) ``` .center[  ] --- # Render basemap -- - Después de incorporar el render de nuestro uiOutput renderizaremos el mapa empleando una vista inicial con la función *setView()* -- ```r #basemap output$map <- renderLeaflet({ leaflet() %>% addProviderTiles(providers$Esri.WorldImagery) %>% #proveedor setView(lng = -71.5, lat = -33.12,zoom = 6) #vista prefijada }) ``` .center[  ] --- # Visualización dinámica de raster -- - Al igual que para los datos vectoriales, emplearemos un *observeEvent({})* que controle los cambios en el proxy de nuestro mapa. -- - El evento deberá ser controlado por *input$fechas* creado en el server y en el, generaremos la simbología de nuestra información y los atributos de visualización. -- ```r observeEvent(input$fechas,{ #select file n <- which(dates==input$fechas)#identificar la posición d ela fecha y la posición del raster #color ramp colores <- c("#ad0000","#ff0000","#ff9901","#fbff01", "#11ff01","#05e8ff","#0519ff","#030d81")#paleta de colores personalizada pal <- colorNumeric(palette = rev(colores), domain = seq(10,25,1), #valores en los que se mueve la temperatura na.color = "transparent")#transparencia para los no data }) ``` -- - El objeto de nombre *"n"* cumplirá el rol de indicar cuál elemento de la lista debe ser desplegado en la aplicación a partir de la función *raster()* --- # Creando archivo raster y mapa reactivo -- ```r observeEvent(input$fechas,{ #select file n <- which(dates==input$fechas)#identificar la posición d ela fecha y la posición del raster #color ramp colores <- c("#ad0000","#ff0000","#ff9901","#fbff01", "#11ff01","#05e8ff","#0519ff","#030d81")#paleta de colores personalizada pal <- colorNumeric(palette = rev(colores), domain = seq(10,25,1), #valores en los que se mueve la temperatura na.color = "transparent")#transparencia para los no data #raster file r <- sst.list[n] %>% raster() #add to map leafletProxy('map') %>% clearControls() %>% clearImages() %>% addRasterImage(r,group = 'MODIS Aqua 4km', method = 'ngb', colors = pal) %>% #agregando raster addLegend(pal = pal, values = seq(10,25,1),title = "SST °C",group = 'Leyenda', position = 'bottomleft') %>% #leyenda en el mapa addLayersControl(overlayGroups = c('MODIS Aqua 4km'),position = 'bottomleft') }) ``` --- class: middle, center  --- # Click reactivo -- - Leaflet proporciona diferentes interacciones con nuestro server. -- - En este caso usaremos la opción reactiva para extraer las coordenadas del punto -- - Crearemos un objeto reactivo mediante *eventReactive()* y la opción *input$map_click* y la colocaremos en nuestro server. -- ```r xy.map <- eventReactive(input$map_click,{ #reactivo de leaflet click <- isolate({input$map_click}) #isolate permite guardar coordenadas y evitar efectos no deseados clat <- click$lat #extraer latitud clng <- click$lng #extraer longitud dts <- SpatialPoints(data.frame(clng,clat), proj4string = CRS("+proj=longlat +datum=WGS84 +no_defs")) #puntos espaciales (sp) dts }) ``` -- - Con la función *SpatialPoints()* convertimos las coordenadas en un objeto espacial, considerando el *EPSG 4326*. Esta es importada al cargar *raster*, ya que llama la librería *sp* -- - El objeto *xy.map()* servirá para poder extraer las series de tiempo que queremos. -- - Para facilitar la extracción de las series usaremos el archivo *pixels.RData* que dispone del valor de cada pixel, con su respectiva coordenada en formato data frame, en un objeto de nombre *tabla* que se genera al leer el archivo en el server. --- # Marcadores reactivos en el mapa -- - Con las coordenadas reactivas, agregaremos marcadores que muestren a los usuarios y usuarias que punto marcaron en el mapa. -- - *Tip:* cada reacción que modifique el mapa (agregar markers, polígonos, rasters, cambio de colores, etc.), es recomendable ponerlo en un *observer distinto*, con el fin de facilitar la localización de posibles errores. -- - Agregue el código a su server después de su observer de raster reactivo ```r # click markers observeEvent(input$map_click,{ click <- input$map_click #captura dinámica del click en la UI clat <- click$lat %>% round(4) #latitud clng <- click$lng %>% round(4) #longitud leafletProxy("map") %>% clearMarkers() %>% #limpiar el proxy addMarkers(lng=click$lng, lat=click$lat,label= paste('lat:',clat,'lng',clng)) #agregar marcador }) ``` --- class: middle, center  --- # Serie de tiempo reactiva -- - Para facilitar la secuencia lógica, crearemos un elemento reactivo que almacene la información del pixel seleccionado. -- - Emplearemos para esto las coordenadas *xy.map()* y la función *cellFromXY()*. -- - Con estos datos extraeremos la información de la tabla para nuestra serie de tiempo en el sitio marcado. -- - Crearemos un objeto de nombre *d.select* al principio de nuestro servidor, después de crear la lista de archivos de SST. Este servirá para dar la referencia de número de filas y columnas de nuestros rasters de entrada ```r #cargar una banda para seleccionar el número de pixel d.select <- sst.list[1] %>% raster() ``` -- - Ahora crearemos el objeto reactivo *r.cell()* que almacenará los valores de la serie para el pixel seleccionado mediante el click reactivo. ```r #Creando vector con la serie r.cell <- reactive({ a1 <- cellFromXY(d.select,xy.map()) #número de la celda o fila de nuestra tabla c <- tabla[a1,3:ncol(tabla)] %>% as.numeric() #serie de datos para esa celda return(c) #devuelve la serie }) ``` --- # Mostrando la serie -- - Para mostrar la serie de manera adecuada, agregaremos en la UI un panel adicional luego de nuestro panel de opciones, que contendrá la salida de nuestro gráfico empleando *plotlyOutput()*. -- ```r #panel para serie de tiempo absolutePanel(id="tSeries", style="z-index:500;background-color: transparent; opacity: 1;margin: auto;border-color: transparent; padding-bottom: 2mm;padding-top: 1mm;", #atributos de estilo class = "panel panel-default", fixed = TRUE,draggable = F, top = 'auto', left = 5, right = 10, bottom = 10,width = '100%', height = "auto", plotlyOutput(outputId = 'ts',height = "250px") # salida gráfica ) ``` --- # Renderizando el gráfico -- - Ahora emplearemos la función *renderPlotly()* para renderizar la serie de tiempo en el server. -- ```r #render plotly output$ts <- renderPlotly({ ts.px <- ts(r.cell(),start = c(2003,1),end = c(2019,12),frequency = 12) #creación de objeto ts g <- ts.px %>% autoplot(ylab = 'SST °C',asp = 0.2)#plot con ggfortify #plot ggplotly(g)#conversión a gráfico dinámico }) ``` -- - Aquí transformamos nuestros valores en un objeto serie de tiempo mediante la función *ts()* considerando: - Inicio y final de la serie con un vector que indica año y mes - Frecuencia de 12 ya que nuestra información es mensual. - Creamos nuestro gráfico con *autoplot()* -- - *ggplotly()* convierte nuestro gráfico estático a un gráfico dinámico --- <center></center> - Seguiremos mejorando nuestra aplicación en la próxima sesión. --- background-image: url(data:image/png;base64,#logo_labgrs_color.png) background-position: center background-size:40%